[reversing] Ransomware¶
IDA¶
manual unpacking¶
먼저 run.exe를 IDA로 확인해봅니다.
public start
start proc near
var_AC = byte ptr -0ACh
pusha
mov esi, offset dword_ECD000
lea edi, [esi-0ACC000h]
push edi
jmp short loc_ECECFA
.......
push edi
call ebp
pop eax
popa // 브레이킹 포인트
lea eax, [esp+2Ch+var_AC]
loc_ECEE7A: ; CODE XREF: start+19Ej
push 0
cmp esp, eax
jnz short loc_ECEE7A
sub esp, 0FFFFFF80h
jmp near ptr byte_44AC9B
- start 함수 부분에 pusha가 있는 것으로 보아 UPX로 패킹되어 있는 것으로 보이며, popa를 찾아 브레이킹 포인트를 걸어둡니다.
- step over를 진행하여 점프 명령까지 실행되면 UPX 언패킹된 상태의 코드를 확인할 수 있습니다.
- 언패킹된 상태의 코드를 "Take memory snapshot"을 통해 저장합니다.
※ Edit -> Plugins -> Universal PE unpacker 로도 가능
dummy code -> nop¶
언패킹시 output window에 아래와 같은 메시지가 출력되는데 더미 코드상에서 popad, pushad 등으로 스택에 저장했다 빼는 행위가 자주 반복되어 발생됩니다.
또한, .. code-block:: console
4135E0: too many stack points have been declared 4135E0: too many stack points have been declared 4135E0: too many stack points have been declared
아래 더미 코드를 nop로 수정하여 해당 메시지가 발생되지 않도록 합니다.
pusha
popa
nop
push eax
pop eax
push ebx
pop ebx
6061905058535B -> 90909090909090
main¶
IDA hexray decompiler로 확인해봅니다.
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // kr00_4@1
FILE *v5; // [sp+1Ch] [bp-14h]@10
unsigned __int32 v6; // [sp+20h] [bp-10h]@4
int v7; // [sp+28h] [bp-8h]@1
unsigned int i; // [sp+28h] [bp-8h]@7
unsigned __int32 j; // [sp+28h] [bp-8h]@10
FILE *File; // [sp+2Ch] [bp-4h]@1
*(_BYTE *)(*(_DWORD *)(__readfsdword(24) + 48) + 2) = 68;
CreateThread(0, 0, StartAddress, 0, 0, 0);
printf("나는 나쁜놈이다!\n나는 매우 나쁘기 때문에 너의 파일을 암호화했다!\n너의 파일을 복구하고 싶다면 5천억 달러를 입금하고 받은 키값으로 파일을 복구해라!\n\n");
printf("Key : ");
sub_401000();
scanf("%s", byte_44D370); // 입력값
v3 = strlen(byte_44D370); // 입력값 길이
sub_401000();
v7 = 0;
File = fopen("file", "rb"); // file 읽기
sub_401000();
if ( !File )
{
sub_401000();
printf("\n\n\n파일을 찾을수 없다!\n");
sub_401000();
exit(0);
}
fseek(File, 0, 2);
sub_401000();
v6 = ftell(File); // 바이트 수 계산
sub_401000();
rewind(File); // 파일 읽기/쓰기 위치를 처음 위치로 이동
sub_401000();
while ( !feof(File) )
{
sub_401000();
byte_5415B8[v7] = fgetc(File);
sub_401000();
++v7;
sub_401000();
}
sub_401000();
for ( i = 0; i < v6; ++i )
{
byte_5415B8[i] ^= byte_44D370[i % v3];
sub_401000();
byte_5415B8[i] = ~byte_5415B8[i];
sub_401000();
}
fclose(File);
sub_401000();
v5 = fopen("file", "wb");
sub_401000();
sub_401000();
for ( j = 0; j < v6; ++j )
{
fputc(byte_5415B8[j], v5);
sub_401000();
}
printf("\n파일을 복구했다!\n나는 몹시 나쁘지만 약속은 지키는 사나이다!\n따라서 너가 나에게 돈을 줬고, 올바른 키값을 받았다면 파일은 정상화 되어 있을 것이다!\n하지만 만약 잘못된 키를 넣었다면 나는 아주아주 나쁘기 때문에 너의 파일은 또 망가질 것이다!");
sub_401000();
return getch();
}
암호화된 file을 열어 입력한 키 값을 이용하여 복호화를 진행합니다.
readme.txt 파일 내용을 보면 Decrypt File이 EXE라고 되어 있으니, 복호화될 파일의 헤더 부분이 4d 5a 90 00 으로 이루어질 것이라고 추측할 수 있습니다.
Decrypt File (EXE)
By Pyutic
calculate¶
디코딩 결과 키 값을 확인할 수 있습니다.
# byte_5415B8[i] ^= byte_44D370[i % v3];
# byte_5415B8[i] = ~byte_5415B8[i];
output_file = ['4D', '5A', '90', '00', '03', '00', '00', '00', '04', '00', '00', '00', 'FF', 'FF', '00', '00']
byte_5415b8 = ['DE', 'C0', '1B', '8C', '8C', '93', '9E', '86', '98', '97', '9A', '8C', '73', '6C', '9A', '8B']
key = ''
for l in range(16):
output_file[l] = ord(output_file[l].decode('hex'))
byte_5415b8[l] = ord(byte_5415b8[l].decode('hex'))
byte_5415b8[l] = byte_5415b8[l] ^ 0xFF
key += chr(byte_5415b8[l] ^ output_file[l])
print key